#include "ShowFrameOpenGLWidget.h"

/**
  问题记录：另一个线程调用gl函数直接程序崩溃
  原因:调用当前窗口.show时，就初始化了openGL上下文，此时你只能在当前线程调用gl函数（也就是gui线程），开启另一个线程调用当然崩溃
  解决1:利用定时器，在initializeGL函数中调用timer->start(1000/frameRate);这样每隔一段时间，就会调用gl函数，并且调用线程是发送信号的线程
  connect(timer,&QTimer::timeout,this,&ShowFrameOpenGLWidget::beginUpdateFrame);可以看到，发送信号的线程就是GUI线程
  解决2（暂未实现）:另一个线程调用gl函数之前，在当前线程重新初始化openGL上下文？？？可行吗

  总结：openGL线程不安全，只能在初始化了openGL线程的线程里使用gl函数

*/



GLfloat vertices[] = {
    1.0f,  1.0f, 0.0f,1.0f,0.0f,0.0f,1,0,  // 左上角
    1.0f,  -1.0f, 0.0f,0.0f,1.0f,0.0f,1,1,  // 右上角
    -1.0f, -1.0f, 0.0f,0.0f,0.5f,0.5f,0,1,  // 右下角
    -1.0f, 1.0f, 0.0f,0.0f,0.0f,1.0f,0,0   // 左下角
};

ShowFrameOpenGLWidget::ShowFrameOpenGLWidget(QWidget *parent):QOpenGLWidget(parent)
{
    status = false;
    frameRate = 60;
    w = 1920;
    h = 1080;
    y = new uchar[w*h];
    u = new uchar[w*h];
    v = new uchar[w*h];
    timer = new QTimer();
    connect(timer,&QTimer::timeout,this,&ShowFrameOpenGLWidget::updateFrame);
}

void ShowFrameOpenGLWidget::initializeGL()
{
    cout<<"initializeGL...."<<endl;
    initializeOpenGLFunctions();
    creatVertex();
    creatTexture();
    creatProgram();
}

void ShowFrameOpenGLWidget::paintGL()
{
    glClearColor(0.2f,0.3f,0.3f,1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
    program.bind();
    yText->bind(0);
    uText->bind(1);
    vText->bind(2);
    program.setUniformValue("yTexture",0);
    program.setUniformValue("uTexture",1);
    program.setUniformValue("vTexture",2);
    glBindBuffer(GL_ARRAY_BUFFER, VBO);
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}

void ShowFrameOpenGLWidget::creatVertex()
{
    // 创建顶点数据
    //1 创建VAO、VBO
    //glGenVertexArrays(1,&VAO);
    glGenBuffers(1,&VBO);
    //2 绑定VAO、VBO
    //glBindVertexArray(VAO);
    glBindBuffer(GL_ARRAY_BUFFER,VBO);
    //3 向缓冲区加入数据
    glBufferData(GL_ARRAY_BUFFER,sizeof(vertices),vertices,GL_STATIC_DRAW);
    //4 告知显卡如何解析缓冲区的数据
    glVertexAttribPointer(0,3,GL_FLOAT,GL_FALSE,8*sizeof(float),(void*)0);
    glEnableVertexAttribArray(0);

    glVertexAttribPointer(1,3,GL_FLOAT,GL_FALSE,8*sizeof(float),(void*)(3*sizeof(float)));
    glEnableVertexAttribArray(1);

    glVertexAttribPointer(2,3,GL_FLOAT,GL_FALSE,8*sizeof(float),(void*)(6*sizeof(float)));
    glEnableVertexAttribArray(2);
}

void ShowFrameOpenGLWidget::creatTexture()
{
    //创建y纹理
    yText = new QOpenGLTexture(QOpenGLTexture::Target2D);
    yText->create();
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,yText->textureId());
    glTexImage2D(GL_TEXTURE_2D,0,GL_RED,w,h,0,GL_RED,GL_UNSIGNED_BYTE,y);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    //创建u纹理
    uText = new QOpenGLTexture(QOpenGLTexture::Target2D);
    uText->create();
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,uText->textureId());
    glTexImage2D(GL_TEXTURE_2D,0,GL_RED,w,h,0,GL_RED,GL_UNSIGNED_BYTE,u);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    //创建v纹理
    vText = new QOpenGLTexture(QOpenGLTexture::Target2D);
    vText->create();
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D,vText->textureId());
    glTexImage2D(GL_TEXTURE_2D,0,GL_RED,w,h,0,GL_RED,GL_UNSIGNED_BYTE,v);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

}

void ShowFrameOpenGLWidget::creatProgram()
{
    program.addShaderFromSourceFile(QOpenGLShader::Vertex,"/home/hzrot/work/project/todesk/MyToDesk_Client/shapes.vert");
    program.addShaderFromSourceFile(QOpenGLShader::Fragment,"/home/hzrot/work/project/todesk/MyToDesk_Client/shapes.frag");
    program.link();
    program.setUniformValue("yTexture",0);
    program.setUniformValue("uTexture",1);
    program.setUniformValue("vTexture",2);
}

void ShowFrameOpenGLWidget::beginUpdateFrame()
{
    timer->start(1000/100);
}

void ShowFrameOpenGLWidget::updateFrame()
{
    //cout<<"xsadas"<<endl;
    if(RecYuvstreams.Size() == 0){
        return;
    }
    //cout<<"渲染"<<endl;
    YuvBuffer* yuvbuffer = RecYuvstreams.Front();
    RecYuvstreams.Pop();
    if(yuvbuffer->uSize == 0.25*w*h){
        genuv(yuvbuffer->u,u);
        genuv(yuvbuffer->v,v);
    }
    //重新绘制纹理
    glBindTexture(GL_TEXTURE_2D,yText->textureId());
    glTexImage2D(GL_TEXTURE_2D,0,GL_RED,w,h,0,GL_RED,GL_UNSIGNED_BYTE,yuvbuffer->y);
    glBindTexture(GL_TEXTURE_2D,uText->textureId());
    glTexImage2D(GL_TEXTURE_2D,0,GL_RED,w,h,0,GL_RED,GL_UNSIGNED_BYTE,u);
    glBindTexture(GL_TEXTURE_2D,vText->textureId());
    glTexImage2D(GL_TEXTURE_2D,0,GL_RED,w,h,0,GL_RED,GL_UNSIGNED_BYTE,v);
    //更新重绘
    update();
    delete  yuvbuffer;
}

void ShowFrameOpenGLWidget::endUpdateFrame()
{
    //1 停止渲染
    timer->stop();
    //2 清空getYuvbuffers
    RecYuvstreams.Clear();
}

void ShowFrameOpenGLWidget::genuv(uchar *uv, uchar *newuv)
{
    for(int i = 0;i<h/2;i++){                              //(i,j)->(i*2,j*2) (i*2+1,j*2)
        for(int j = 0;j<w/2;j++){
            newuv[i*2*w + j*2] = uv[i*(w/2) +j];
            newuv[(i*2+1)*w + j*2] = uv[i*(w/2) +j];
            newuv[(i*2+1)*w + j*2+1] = uv[i*(w/2) +j];
            newuv[i*2*w + j*2+1] = uv[i*(w/2) +j];
        }
    }
}
